//
// AppInterface.h
// libprotea
// Tools Library for the Protea Project / Application Handler Interface
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
// AppInterface.h
//
#ifndef __PROTEA_APPINTERFACE_H__
#define __PROTEA_APPINTERFACE_H__


// Forward declarations
class XCTransactionInfo;
class XCTxn;
class XCDBDriverDescr;
class XCSharedLibrary;
class XCTransactionVars;
class security_info_manager;

#define NOT_IN_LIT_GROUP -65535


/**
	\file AppInterface.h
	\brief Application base class

	Contains all the functionality needed for a Protea application.
	
	Steps for application creation:
		- All tools applications should derive from XCAppInterface
		- Override the XCAppInterface::GotMessage function

	Main function:
		- Call Initialize
		- Call StartApp (we will always be a child)
		- Enter the message loop
*/

#include "../Appdic/appdic.h"
#include "../OSDep/port.h"
#include "../OSDep/process.h"
#include "message.h"
#include "security.h"
#include <stddef.h>

#define PIPE_READ	0
#define PIPE_WRITE	1

// Forward class definitions
class XCField;
class XCBuffer;
class XCDBHandler;
class XCBufferList;
class XCStatedTxn;
class XCStatedTxnList;
class XCTransactionVars;
class XCDBStatement;

/// Trace Categories
enum trace_categories {
	/// Buffer
	TRC_BUFFER = 1,
	/// Field
	TRC_FIELD = 2,
	/// Errors
	TRC_ERROR = 4,
	/// Debug Dumps
	TRC_NOLABEL = 8,
	/// User exits
	TRC_USEREXIT = 16,
	/// Database
	TRC_DATABASE = 32,
	/// Messaging
	TRC_MESSAGING = 64,
	/// App Interface
	TRC_APPINTERFACE = 128,
	/// Traffic
	TRC_TRAFFIC = 256,
	/// Functional trace
	TRC_FUNCTIONS = 512
};

/// Trace Category Levels
enum trace_category_levels {
	/// Buffer
	TCL_BUFFER = 0,
	/// Field
	TCL_FIELD = 1,
	/// Error
	TCL_ERROR = 2,
	/// Debug dump
	TCL_NOLABEL = 3,
	/// User exits
	TCL_USEREXIT = 4,
	/// Database
	TCL_DATABASE = 5,
	/// Messaging
	TCL_MESSAGING = 6,
	/// AppInterface
	TCL_APPINTERFACE = 7,
	/// Traffic functions
	TCL_TRAFFIC = 8,
	/// Functional tracing
	TCL_FUNCTIONS = 9
};

/// Trace Levels
enum trace_levels {
	TLV_ALWAYS = 0,
	TLV_LOW = 50,
	TLV_MEDIUM = 100,
	TLV_HIGH = 150,
	TLV_EVERYTHING = 255
};

// Application options
class __EXPORT XCAppOptions : public XCBase {
public:
	XCAppOptions();

	int version;

	// Appdic loading options

	/// Do not load database handler
	bool no_database;
	bool no_transactions;
	bool appdic_first_run;

	/// Debug filter mask
	int debug_type_filter;
	/// Debug cutoff level
	int debug_level_cutoff[ num_tc ];
};

/// Enumeration of possible DB_Relate types
enum dbr_type 
{
	/// Field is a primary key
	dbr_primary_key = 1,
	/// Field is unique
	dbr_unique_key = 3,
	/// Field is indexed
	dbr_index_key = 4,
	/// Field is foreign key
	dbr_foreign_key = 5,
	/// Field is an autonumber field
	dbr_autonumber = 6,
	dbr_default = 100		// Default constraint only used by prep for SQL Server
};


/// Error severity level
enum error_severity {
	/// Status message
	SEV_STATUS = -1,
	/// Warning message
	SEV_WARNING = 0,
	/// Unrecoverable error
	SEV_ERROR = 1,
	/// Critical error, immediate exit
	SEV_CRITICAL = 2
};




// Maintains a list of active super transactions
struct superlist {
	long id;
	XCTxn* trans;
	superlist* next;
};



// Collection of application settings
class __EXPORT AppSettings {
public:
	AppSettings() {
		smtp = NULL;
		printer = NULL;
		admin_email = NULL;
		fop_location = NULL;
	};

	~AppSettings() {
		if (smtp)
			delete [] smtp;
		if (printer)
			delete [] printer;
		if (admin_email)
			delete [] admin_email;
		if (fop_location)
			delete [] fop_location;
	};

	char* smtp;
	char* printer;
	char* admin_email;
	char* fop_location;
};


/////////////////////////////////////////////////////////////////////////////
// XCAppInterface

/**
	\brief Application interface class

	Starting point for all application activities
*/
class __EXPORT XCAppInterface : public XCBase, public XCAuthenticate, public AppSettings {
	friend class XCAppInterfaceExternal;
	friend class XCBuffer;
	friend class XCDBHandler;
	friend class XCSQLGen;
	friend class XCTxn;	
	friend class XCStatedTxnList;
	friend class security_info_manager;

public:
	/////////////////////////////
	//
	// External functions - need to document
	//
	/////////////////////////////

	/**
		\brief Create a buffer using the supplied FormatNumber
		\param FormatNumber	Format number of the buffer to be created
		\param load_defaults Determines whether default values are loaded

		Returns the newly created buffer
	*/
	XCBuffer* CreateBuffer( long FormatNumber, bool load_defaults=false );

	/**
		\brief Fires a user-exit
		\param ue_num User exit number
		\param buffer_list List of buffers to be passed to the user-exit
		\param param Additional parameter

		This function attempts to execute the supplied user-exit number.  If this 
		user-exit is disabled, then the function just exits.  If it is enabled,
		it will attempt to locate the user exit function and execute.

		Errors:
			ERR_NO_UE_LIB			- The user exit library did not exist at startup
			ERR_NO_UE_FUNCTION		- The specified user exit didn't exist in the user exit library
			ERR_USER_EXIT_MESSAGE	- User exit returned message
	*/
	int TriggerUserExit( long ue_num, XCBufferList* buffer_list, XCTransactionVars* errors);

	/**
		\brief Logs a message
		\param category Defines the category a message belongs to
		\param level	Defines the severity level
		\param lpszFormat	Format string to display (printf style)
		\param ... Additional parameters

		Messages are logged through UDP port 4025, to the console and to a file
	*/
	void ToolsTrace( trace_categories category, int level, const char* lpszFormat, ...);

	/**
		\brief Returns a string containing the current locale
		Example: en_US
	*/
	const char* GetLocale();

	/**
		\brief Get process name
		Returns the current process name (as given in Initialize)
	*/
	const char* GetProcessName() const;

	/**
		\brief Returns a literal value
		\param LiteralNum Literal number to return

		If the literal is found in the current language, it returns a pointer to an
		XCField object.  Otherwise, it returns NULL.
	*/
	XCField* GetLiteral( long LiteralNum );

	/**
		\brief Returns a system value
		\param SysValNum System value number to return

		If the system value is found, it returns a pointer to an XCField object. Otherwise,
		it returns NULL.
	*/
	XCField* GetSysValue( long SysValNum );

	/** 
		\brief Lookup a message in the message catalog
		\param number Message number
		\param lang_id Language Id

		Returns a pointer to the catalog message.  The function strdup is used to allocate
		storage for the message, and needs to be freed after use.

		If the specified message number cannot be found for the specified language, NULL
		will be returned.
	*/
	char* GetCatalogMsg( int number, int lang_id = 0 );
	
	/**
		\brief Sends message to system admin
		\param errnum Error number
		\param field Field in error
		\param parmcount Number of additional parameters
		\param ... Additional parameters
	*/
	void AdminMsg( const long errnum, long field, long parmcount=0, ... );

	/**
		\brief Returns the current application number
	*/
	long GetAppNumber() const;

	/**
		\brief Cleans up buffers/db connections, etc.
		\param bufcount Number of objects supplied
		\param ... XCBase** objects to delete
		All Protea objects are based on the XCBase class.  This function will call the
		release function for each object and then set the 
	*/
	void CleanupObjects( int bufcount, ... );

	/**
		\brief Send a Transaction Response with data
		\param transin Transaction info object
		\param data Data to send
		\param size Size of the data to send
		\param format Format number
		\param type Type
	*/
	void TransResponse( XCTransactionInfo* transin, const void* data, int size, int format, trans_data_type type );

	/**
		\brief Send a Transaction Response with buffer data
	*/
	void TransResponse( XCTransactionInfo* transin, XCBuffer* buffer, trans_data_type type );

	/**
		\brief Send a Transaction Response with a message
	*/
	void TransResponseMsg( XCTransactionInfo* transin, int number, const char* message, int buffer=0, int field=0, error_severity severity = SEV_STATUS);

public:
	//////////////////////////////////
	//
	// INTERNAL ROUTINES
	//
	// THESE SHOULD NOT BE DOCUMENTED
	//
	//////////////////////////////////
	/* 
		\brief Constructor
	*/
	XCAppInterface();

	/*
		\brief Destructor
	*/
	virtual ~XCAppInterface();

	/*
		\brief Load infomation for a particular application
		\param process_name Debugging purposes
		\param appnumber Application number to load
			- 0 = Appl_defn and Appl_interface only
			- >0 = Specific application
			- <0 = Is the same as above, but loads Appl_defn and appl_interface as well.
		\param FileExtension File extension to append (used only in prep)

		Initializes the application information:
			- Loads prep information for an application
			- Connects to necessary databases.
			- Opens user exit shared library

		Exceptions:
			- ERR_BAD_INIT	Unable to read a file
			- ERR_DB_ERROR	Database errors

		Return value:
			Always zero.
	*/
	int Initialize(char* process_name, int appnumber, char* FileExtension = NULL);

	// Initialize User exits
	bool InitializeUE();

	// Unload information
	void Uninit();

	/*
		\brief Create a buffer using supplied data
		\param FormatNumber	Format number of the buffer to be created
		\param new_data Pointer to the data
		Returns the newly created buffer.  This buffer contains a copy of the original
		data.
	*/
	XCBuffer* CreateDataBuffer( long FormatNumber, void* new_data );

	/*
		\brief Sends a group of transaction messages to a destination port
		\param txn Pointer to transaction data
		\param dest Destination port
		\param reply Message is a reply to this message
		\param app Application number
	*/
	void SendTransaction( XCTransactionInfo* txn, XCMsgPortPtr dest, XCToolsMessage* reply = NULL, long app = 0, route_loc src = src_int );

	void ProcessingSpecs() {;};

	// Contains $XSIROOT
	char* root;

	// Path to Tools/Appdic
	char ToolsDir[ MAX_PATH ];

	// Queries the current application options
	void GetOptions( XCAppOptions& options );

	// Sets the current application options
	void SetOptions( XCAppOptions& options );

	// Returns a pointer to the database handler
	XCDBHandler* GetDatabase();

	// Start debug logging
	void StartLogging( char* process_name );

	// Stop debug logging
	void StopLogging();

	int GetTraceIndex( trace_categories category );

	/*
		\brief Create a dynamic field
		\param new_name dynamic buffer and field name
		\param new_length Length of field
		\param new_dec_places Number of decimal places
		\param new_type Data type
		\param new_min_len Minimum length

		For an example of the use of this function, see IsValidType in customizer.cpp

		Returns a newly created buffer with new_name containing a single dynamic field numbered as -1.
	*/
	XCBuffer* CreateDynamicField( char* new_name, long new_length, int new_dec_places, int new_type, int new_min_len );

	// Enters the application message loop
	THREAD_HANDLE MessageLoop( route_loc dest = dest_app, bool thread = false );

	// Starts a new message loop
	void CancelMessageLoop();

	// Aborts the current message loop and closes all open ports
	void StartMessageLoop( route_loc dest );

	// Leave these undocumented for now
	XCBuffer* CreateDynamicBuffer( const long *FieldNumbers, bool Load_Defaults, long FormatNumber = 0 );
	XCBuffer* CreateDynamicBuffer( int count, const buffer_ds*, long FormatNumber = 0 );
	XCBuffer* CreateDynamicBuffer( buffer_format* );

	// Returns the status of the message loop abort flag
	bool GetAbortFlag();

	void GetMessage( XCMsgPortPtr parent, XCMsgPortPtr* childlist, XCMsgPortPtr listener );
	void SendMessage( XCMsgPortPtr src, XCMsgPortPtr dest, XCToolsMessage* value );
	void BroadcastMessage( XCMsgPortPtr src, XCToolsMessage* value );

	void QuitLoop();
	int StartApp( PORT_HANDLE in, long type, init_message& init );
	void ClearPorts( bool force );
	int MainFunc( int socklisten );

	// Create a child procress
	XCMsgPortPtr CreateChild( char* app, long pcount = 0, char** parms = NULL, bool usepipe = true, app_start_message* tempappl = NULL );
	XCMsgPortPtr RemoveChild( XCMsgPortPtr* childlist, XCMsgPortPtr toremove, XCMsgPortPtr prev = NULL );

	// Connect to a remote process
	XCMsgPortPtr Connect( char* host, int port );

	process_info* process_list;
	buffer_format* GetFormatInfo( int format );
	buffer_format* GetFormatInfo( const char* format_name );

	buffer_field* GetFieldInfo( int field );
	buffer_field* GetFieldInfo( const char* field_name );

	transaction_defn* GetTransaction( const char* trans_name );
	transaction_defn* GetTransaction( int trans_number );

	appl_defn* GetApplDefn( int appl_nbr );

	// Process ID of parent process
	long parentpid;

	XCMsgPortPtr GetParent();
	void SetParent( XCMsgPortPtr new_parent );

	// Maintains current function 'level' for TRC_FUNCTION tracing
	long func_level;

	// Returns a pointer into the APPDIC_DATA structure
	const char* FixOffset( long offset ) { if (offset) return APPDIC_DATA + offset; else return NULL; };

	lit_group* GetLitGroup( int number );
	XCField* FromLitGroup( long value, long group );
	long ToLitGroup( const XCField* value, long group );
	long ToLitGroup( const char* value, long group );

	bool tracing;

	// Function performance tracking
	tPerfCounters* perf_counters;

	// Functional tracing on/off
	bool TraceFunctions() { return (options.debug_type_filter & TRC_FUNCTIONS) != 0; };

	// Super transaction stuff
	XCTxn* GetSuper( long number );
	long GetSuper( XCTxn* supertrans );
	superlist* AddSuper();

	XCDBDriverDescr* GetDBDriverDesc( XCBuffer* buffer );

	void SendStatus( route_loc src, XCMsgPortPtr parent, long listener = 0 );

	// This shouldn't be PUBLIC
	XCStatedTxnList* StatedTxnList;		// Stated Transaction List - all current stated txn's

	static THREAD_PROCEDURE StatedTxnCleanupThread( void* parm );

	// Signals that a process has dropped connection
	void DroppedProcess( pid_t child_pid );

	// Signal that we should cleanup all our information (transports)
	void CleanupPorts();

	// Returns a pointer to the abort event
	XCManualEvent* GetAbortEvent()  { return &abort; };

protected:

	// Destination type (gcs, application, interface)
	long mType;

	// Log file
	ofstream *logfile;		// log file

	// Logging port
	XCSocketTransport* logging;

	// Buffer containing all system values
	XCBuffer* system_values_buf;

	// Buffer containing all literal values
	XCBuffer* literal_values_buf[ MAX_LANGUAGES ];
	
	transaction_defn*	transaction_defn_list;
	long				transaction_defn_list_count;

	transactions*		transactions_list;
	long				transactions_list_count;

	appl_defn*			appl_defn_list;
	long				appl_defn_list_count;

	XCDBHandler* db;

	int LoadFile( char* filename, void** data, int recordsize );

	// Checks a port for activity
	int CheckPort( XCMsgPortPtr port, XCPortHandler* ph );

	// Traffic functions
	/*
		\brief Message handling functionality
		\param message Completed message

		In general, this function should not be over-ridden.  The base version provides
		automatic handling of all normal dispatching messages, and packaging of transaction
		messages

		The XCAppInterface::OnTransaction function should be used instead

		Return true if it is ok to delete the message, false if it was already deleted.
	*/
	virtual bool GotMessage( XCMsgPort* message, XCToolsMessage* msg );

	bool HandleOptionMessage( XCMsgPort* port, XCToolsMessage* msg );
	virtual bool HandleDispatchMessage( XCMsgPort* port, XCToolsMessage* msg );
	virtual void HandleSecurityChange( XCToolsMessage* msg );
	virtual bool HandleTransactionMessage( XCMsgPort* port, XCToolsMessage* msg );
	void ForwardMessage( XCMsgPort* port, XCToolsMessage* msg, XCMsgPort* dest_port = NULL );

	/*
		\brief Called when a full transaction is received
		\param txn Completed transaction

		Return value should be the transaction response

		This function should be over-ridden by the application
	*/
public:
	virtual XCTransactionInfo* OnTransaction( XCTransactionInfo* txn );
protected:
	virtual XCTransactionInfo* OnQuery( XCTransactionInfo* query );

	XCTransactionInfo* SystemTransaction( XCTransactionInfo* txn );
	XCTransactionInfo* HandleLogin( XCTransactionInfo* txn );
	XCTransactionInfo* HandleLogout( XCTransactionInfo* txn );
	XCTransactionInfo* CloseTransaction( XCTransactionInfo* txn );

	virtual void OnConnect( XCMsgPort* dest );
	virtual void OnUserMessage( char* message );
	virtual void OnDroppedConnection( XCMsgPortPtr tempport );
	virtual void OnDroppedProcess( pid_t process_pid );

	XCManualEvent abort;
	bool message_loop_running;

	// Accepting connections

	// Listening port
	XCMsgPortPtr sockin;

	// Parent port
	XCMsgPortPtr parent;

	// List of children
	XCMsgPortPtr childlist;
	XCCriticalSection childlistlock;

	int numsocks;
	int highsock;
	fd_set socks;
	timeval timeout;
	XCPortHandler ports;


	// Current set of application options
	XCAppOptions		options;

	// User exit information

	// Handle to user-exit library
	XCSharedLibrary*	uelib;

	// Pointer to user-exit per process storage area
	char*				process_storage;

	int mynum;			// Appinterface number - for debugging prep
	int myappnum;		// Application number - from Initialize

	XCTransactionInfo* TxnList;			// Transaction List - all current queued txn's

	// Current application location type for routing (application, interface, etc...)
	route_loc current_loc;

	const buffer_header* GetAPPDICHeader() { return appdic_header; };

	static THREAD_PROCEDURE MessageThread( void* parm );
	static THREAD_PROCEDURE MessageLoopThread( void* parm );
  
protected:
	void DebugMessageTraffic( char* string, XCMsgPortPtr port );

	// APPDIC data is stored here
	buffer_header* appdic_header;
	char* APPDIC_DATA;
	long APPDIC_DATA_SIZE;

	superlist* supertrans;

	XCCriticalSection trace;
	XCCriticalSection TxnListLock;

	int trans_open;
	int trans_complete;
	int trans_stated;

	int msg_sent;
	int msg_recv;

	char* process;

	int worker_threads;

	void DelTxnFragment( long trans_id );
	XCTransactionInfo* FindTxn( long trans_id, XCTransactionInfo** prev = NULL );
	XCTransactionInfo* AddTxn( XCToolsMessage* msg, XCMsgPort* port );
	void DelTxn( XCTransactionInfo* txn );
};


#endif
